]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
[AppMonitor] Handle window title changes causing mapping changes
authorColin Walters <walters@verbum.org>
Thu, 27 Aug 2009 06:22:25 +0000 (02:22 -0400)
committerColin Walters <walters@verbum.org>
Thu, 27 Aug 2009 06:22:25 +0000 (02:22 -0400)
For Firefox/OpenOffice, right now we have a workaround in the
code where we look at their "title" property.  However, we
weren't monitoring that property for changes, and I'm fairly
certain Firefox at least was mapping a window and then very
quickly changing its title after.  So we need to handle
dynamic changes.

Split out the wm_class mapping from the title hack.  It was
messy and weird to have the two mixed because they're not
at all related, and we're not trying to handle WM_CLASS changes
right now.

Explicitly connect to notify::title in the case where we had
a title fallback.  When a title changes, just treat it as
an add+remove.

In the Application Menu area in the panel, hook up to app-added
and app-removed so we get notification of the active app changing.

js/ui/panel.js
src/shell-app-monitor.c

index f477ea0d32e9bc7c52f0785f7a911dc613a2f36f..b8a3f66a33511dfb02b033ad7b2f7dc6b7383878 100644 (file)
@@ -98,12 +98,17 @@ AppPanelMenu.prototype = {
             this.actor.opacity = 192;
         }));
 
-        this._metaDisplay.connect('notify::focus-window', Lang.bind(this, function () {
-            this._sync();
-        }));
-        Shell.AppMonitor.get_default().connect('startup-sequence-changed', Lang.bind(this, function() {
-            this._sync();
-        }));
+        this._metaDisplay.connect('notify::focus-window', Lang.bind(this, this._sync));
+
+        let appMonitor = Shell.AppMonitor.get_default();
+        appMonitor.connect('startup-sequence-changed', Lang.bind(this, this._sync));
+        // For now just resync on application add/remove; this is mainly to handle
+        // cases where the focused window's application changes without the focus
+        // changing.  An example case is how we map Firefox based on the window
+        // title which is a dynamic property.
+        appMonitor.connect('app-added', Lang.bind(this, this._sync));
+        appMonitor.connect('app-removed', Lang.bind(this, this._sync));
+
         this._sync();
     },
 
index caefcd2a266c7554005550ea1d9b476831fc6272..453a5f53633421a1cd9721cadd8893cb9b4ac7a4 100644 (file)
@@ -92,13 +92,13 @@ static struct
   const char *pattern;
   GRegex *regex;
 } title_patterns[] =  {
-    {"mozilla-firefox", ".* - Mozilla Firefox", NULL}, \
-    {"openoffice.org-writer", ".* - OpenOffice.org Writer$", NULL}, \
-    {"openoffice.org-calc", ".* - OpenOffice.org Calc$", NULL}, \
-    {"openoffice.org-impress", ".* - OpenOffice.org Impress$", NULL}, \
-    {"openoffice.org-draw", ".* - OpenOffice.org Draw$", NULL}, \
-    {"openoffice.org-base", ".* - OpenOffice.org Base$", NULL}, \
-    {"openoffice.org-math", ".* - OpenOffice.org Math$", NULL}, \
+    {"mozilla-firefox.desktop", ".* - Mozilla Firefox", NULL}, \
+    {"openoffice.org-writer.desktop", ".* - OpenOffice.org Writer$", NULL}, \
+    {"openoffice.org-calc.desktop", ".* - OpenOffice.org Calc$", NULL}, \
+    {"openoffice.org-impress.desktop", ".* - OpenOffice.org Impress$", NULL}, \
+    {"openoffice.org-draw.desktop", ".* - OpenOffice.org Draw$", NULL}, \
+    {"openoffice.org-base.desktop", ".* - OpenOffice.org Base$", NULL}, \
+    {"openoffice.org-math.desktop", ".* - OpenOffice.org Math$", NULL}, \
     {NULL, NULL, NULL}
 };
 
@@ -181,6 +181,9 @@ static AppUsage * get_app_usage_for_context_and_id (ShellAppMonitor *monitor,
                                                     const char      *context,
                                                     const char      *appid);
 
+static void track_window (ShellAppMonitor *monitor, MetaWindow *window);
+static void disassociate_window (ShellAppMonitor *monitor, MetaWindow *window);
+
 static gboolean idle_save_application_usage (gpointer data);
 
 static void restore_from_file (ShellAppMonitor *monitor);
@@ -257,15 +260,20 @@ destroy_usage (AppUsage *usage)
   g_free (usage);
 }
 
-static char *
-get_wmclass_for_window (MetaWindow   *window)
+/**
+ * get_app_id_from_title:
+ *
+ * Use a window's "title" property to determine an application ID.
+ * This is a temporary crutch for a few applications until we get
+ * them correctly setting their WM_CLASS.
+ */
+static const char *
+get_app_id_from_title (MetaWindow   *window)
 {
   static gboolean patterns_initialized = FALSE;
-  const char *wm_class;
   char *title;
   int i;
 
-  wm_class = meta_window_get_wm_class (window);
   g_object_get (window, "title", &title, NULL);
 
   if (!patterns_initialized) /* Generate match patterns once for all */
@@ -285,15 +293,15 @@ get_wmclass_for_window (MetaWindow   *window)
         {
           if (g_regex_match (title_patterns[i].regex, title, 0, NULL))
             {
+              g_free (title);
               /* Set a pseudo WM class, handled like true ones */
-              wm_class = title_patterns[i].app_id;
-              break;
+              return title_patterns[i].app_id;
             }
         }
     }
 
   g_free (title);
-  return g_strdup (wm_class);
+  return NULL;
 }
 
 /**
@@ -306,21 +314,20 @@ get_wmclass_for_window (MetaWindow   *window)
 static char *
 get_cleaned_wmclass_for_window (MetaWindow  *window)
 {
-  char *wmclass;
+  const char *wmclass;
   char *cleaned_wmclass;
 
-  wmclass = get_wmclass_for_window (window);
+  wmclass = meta_window_get_wm_class (window);
   if (!wmclass)
     return NULL;
 
   cleaned_wmclass = g_utf8_strdown (wmclass, -1);
-  g_free (wmclass);
+
   /* This handles "Fedora Eclipse", probably others.
    * Note g_strdelimit is modify-in-place. */
   g_strdelimit (cleaned_wmclass, " ", '-');
-  wmclass = g_strdup (cleaned_wmclass);
-  g_free (cleaned_wmclass);
-  return wmclass;
+
+  return cleaned_wmclass;
 }
 
 /**
@@ -422,7 +429,14 @@ get_app_for_window_direct (MetaWindow  *window)
   g_free (with_desktop);
 
   if (result == NULL)
-    result = create_transient_app_for_window (window);
+    {
+      const char *id = get_app_id_from_title (window);
+
+      if (id != NULL)
+        result = shell_app_system_load_from_desktop_file (appsys, id, NULL);
+      else
+        result = create_transient_app_for_window (window);
+    }
   return result;
 }
 
@@ -553,7 +567,6 @@ get_active_window (ShellAppMonitor *monitor)
   return NULL;
 }
 
-
 typedef struct {
   gboolean in_context;
   GHashTableIter context_iter;
@@ -691,6 +704,36 @@ reset_usage (ShellAppMonitor *self,
     }
 }
 
+static void
+on_transient_window_title_changed (MetaWindow      *window,
+                                   GParamSpec      *spec,
+                                   ShellAppMonitor *self)
+{
+  ShellAppSystem *appsys;
+  ShellAppInfo *current_app;
+  ShellAppInfo *new_app;
+  const char *id;
+
+  current_app = g_hash_table_lookup (self->window_to_app, window);
+  /* Can't have lost the app */
+  g_assert (current_app != NULL);
+
+  /* Check if we now have a mapping using the window title */
+  id = get_app_id_from_title (window);
+  if (id == NULL)
+    return;
+
+  appsys = shell_app_system_get_default ();
+  new_app = shell_app_system_load_from_desktop_file (appsys, id, NULL);
+  if (new_app == NULL)
+    return;
+  shell_app_info_unref (new_app);
+
+  /* It's simplest to just treat this as a remove + add. */
+  disassociate_window (self, window);
+  track_window (self, window);
+}
+
 static void
 track_window (ShellAppMonitor *self,
               MetaWindow      *window)
@@ -719,6 +762,16 @@ track_window (ShellAppMonitor *self,
   usage = get_app_usage_from_window (self, window);
   usage->transient = shell_app_info_is_transient (app);
 
+  if (usage->transient)
+    {
+      /* For a transient application, it's possible one of our title regexps
+       * will match at a later time, i.e. the application may not have set
+       * its title fully at the time it initially maps a window.  Watch
+       * for title changes and recompute the app.
+       */
+      g_signal_connect (window, "notify::title", G_CALLBACK (on_transient_window_title_changed), self);
+    }
+
   /* Keep track of the number of windows open for this app, when it
    * switches between 0 and 1 we emit an app-added signal.
    */
@@ -743,12 +796,11 @@ shell_app_monitor_on_window_added (MetaWorkspace   *workspace,
   track_window (self, window);
 }
 
+
 static void
-shell_app_monitor_on_window_removed (MetaWorkspace   *workspace,
-                                     MetaWindow      *window,
-                                     gpointer         user_data)
+disassociate_window (ShellAppMonitor   *self,
+                     MetaWindow        *window)
 {
-  ShellAppMonitor *self = SHELL_APP_MONITOR (user_data);
   ShellAppInfo *app;
 
   app = g_hash_table_lookup (self->window_to_app, window);
@@ -785,6 +837,14 @@ shell_app_monitor_on_window_removed (MetaWorkspace   *workspace,
   shell_app_info_unref (app);
 }
 
+static void
+shell_app_monitor_on_window_removed (MetaWorkspace   *workspace,
+                                     MetaWindow      *window,
+                                     gpointer         user_data)
+{
+  disassociate_window (SHELL_APP_MONITOR (user_data), window);
+}
+
 static void
 load_initial_windows (ShellAppMonitor *monitor)
 {